package com.yt.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.yt.server.utils.PropUtils;

/**
 * 安全策略验证服务, 基于国外网站关于Flex安全策略验证的代码做了些许调整.
 */
@Component("PolicyServer")
public class PolicyServer extends Thread {

	protected static Logger log = LoggerFactory.getLogger(GlobalScope.class);

	// 监听端口
	private int policyServerPort;

	// 策略文件路径(相对路径)
	private String policyFile;

	// 线程池维护线程的最少数量
	private int threadPoolSizeMin;

	// 线程池维护线程的最大数量
	private int threadPoolSizeMax;

	// 线程池维护线程所允许的空闲时间(秒)
	private int threadMaxIdleSeconds;

	// 线程池状态日志记录开关
	private String poolStatusLogSwitch;

	// 线程池状态记录间隔时间(秒)
	private int poolStatusLogInterval;

	// Socket超时时间(秒)
	private int socketTimeout;

	private ServerSocket serverSocket;

	// 策略验证标识
	private String policyServerRequest;

	// 线程池
	private ThreadPoolExecutor threadPool;

	// 当前策略文件内容
	private String policy;

	// 线程池状态记录定时器
	private Timer timer;

	// 当前运行状态
	private boolean running = false;

	public PolicyServer() {
		this.start();
	}

	/**
	 * 初始化配置
	 * 
	 * @throws Exception
	 */
	private void initialize() throws Exception {
		// 加载相关配置内容
		loadConfiguration();
		this.policyServerRequest = "<policy-file-request/>";
		// 读取策略文件
		this.policy = readPolicyFile(policyFile);
		// 初始化线程池
		this.threadPool = new ThreadPoolExecutor(threadPoolSizeMin,
				threadPoolSizeMax, threadMaxIdleSeconds, TimeUnit.SECONDS,
				new LinkedBlockingQueue<Runnable>());
		if ("on".equals(poolStatusLogSwitch)) {
			// 开启线程池状态记录定时器
			this.timer = new Timer("PoolStatusLogger");
			this.timer.schedule(new PoolStatusLogger(), 100L,
					this.poolStatusLogInterval);
		}
		this.running = true;
	}

	/**
	 * 释放连接
	 * 
	 */
	public void releaseConnection() {
		try {
			this.running = false;
			if (timer != null) {
				timer.cancel();
				timer = null;
			}
			if (serverSocket != null) {
				serverSocket.close();
				serverSocket = null;
			}
			if (threadPool != null) {
				threadPool.shutdownNow();
				threadPool = null;
			}
		} catch (Exception e) {
			log.error("释放端口(" + policyServerPort + ")连接出错，原因：", e);
		}
	}

	/**
	 * 加载相关配置内容
	 */
	private void loadConfiguration() {
		this.policyServerPort = getProperty("policy_server_port", 843);
		this.policyFile = getProperty("policy_file", "conf/default_policy.xml");
		this.threadPoolSizeMin = getProperty("thread_pool_size_min", 5);
		this.threadPoolSizeMax = getProperty("thread_pool_size_max", 10);
		this.threadMaxIdleSeconds = getProperty("thread_max_idle_seconds", 10);

		this.poolStatusLogSwitch = getProperty("pool_status_log_switch", "off");
		this.poolStatusLogInterval = getProperty(
				"pool_status_log_interval_seconds", 60) * 1000;
		this.socketTimeout = getProperty("socket_timeout_seconds", 10) * 1000;
	}

	/**
	 * 获取属性值
	 * 
	 * @param key
	 *            属性键
	 * @param defaultValue
	 *            默认值
	 * @return
	 */
	private int getProperty(String key, int defaultValue) {
		Integer value = Integer.valueOf(PropUtils.instance().getProperty(key));
		if (value == null) {
			return defaultValue;
		}
		return value;
	}

	/**
	 * 获取属性值
	 * 
	 * @param key
	 *            属性键
	 * @param defaultValue
	 *            默认值
	 * @return
	 */
	private String getProperty(String key, String defaultValue) {
		String value = PropUtils.instance().getProperty(key);
		if (value == null) {
			return defaultValue;
		}
		return value;
	}

	/**
	 * 读取策略文件
	 * 
	 * @param path
	 *            文件路径
	 * @return
	 */
	private String readPolicyFile(String path) {
		StringBuffer content = new StringBuffer();
		try {
			InputStream is = this.getClass().getClassLoader()
					.getResourceAsStream(path);
			BufferedReader br = null;
			try {
				br = new BufferedReader(new InputStreamReader(is));
				String line = null;
				while ((line = br.readLine()) != null) {
					content.append(line);
					content.append(System.getProperty("line.separator"));
				}
			} finally {
				if (br != null) {
					br.close();
				}
				if (is != null) {
					is.close();
				}
			}
		} catch (IOException ioe) {
			log.error("读取策略文件内容出错，原因：", ioe);
		}
		return content.toString();
	}

	/**
	 * 判断当前服务是否已经运行
	 * 
	 * @return
	 */
	private boolean isRunning() {
		try {
			ServerSocket server = new ServerSocket(policyServerPort);
			server.close();
			return false;
		} catch (IOException ioe) {
			return true;
		}
	}

	public void run() {
		if (isRunning()) { // 如果已经运行，则不重新开启
			return;
		}
		try {
			// 初始化配置
			initialize();
			// 开启监听服务
			serverSocket = new ServerSocket(policyServerPort);
			log.info("PolicyServer listening on port " + policyServerPort);
			Socket socket = null;
			System.out.println("PolicyServer inited.");
			while (running) {
				if (serverSocket != null && serverSocket.isClosed()) {
					break;
				}
				socket = serverSocket.accept();
				try {
					threadPool.execute(new SocketHandler(socket));
				} catch (Exception e) {
					log.error("开启安全策略验证线程出错，原因：", e);
				}
				try {
					Thread.sleep(10L);
				} catch (InterruptedException ie) {
					log.error("当前线程出现异常：", ie);
				}
			}
		} catch (Exception e) {
			log.error("安全策略验证启动出错，原因：", e);
		} finally {
			if (timer != null) {
				timer.cancel();
			}
			if (threadPool != null) {
				threadPool.shutdownNow();
			}
		}
	}

	/**
	 * 处理Socket业务类
	 */
	private class SocketHandler implements Runnable {
		private Socket socket;
		private BufferedReader socketIn;
		private PrintWriter socketOut;

		public SocketHandler(Socket socket) {
			this.socket = socket;
		}

		public void run() {
			try {
				// 设置socket超时时间
				socket.setSoTimeout(socketTimeout);
				socketIn = new BufferedReader(new InputStreamReader(
						socket.getInputStream()));
				socketOut = new PrintWriter(socket.getOutputStream(), true);
				// 读取并响应策略验证
				readPolicyRequest();
			} catch (IOException ioe) {
				log.error("验证安全策略文件出错，原因：", ioe);
			} finally {
				close();
			}
		}

		/**
		 * 读取并响应策略验证
		 */
		private void readPolicyRequest() {
			try {
				if (read().startsWith(policyServerRequest)) {
					write(policy);
				}
			} catch (IOException ioe) {
				log.error("读取并响应安全策略验证出错，原因：", ioe);
			}
		}

		/**
		 * 读取客户端内容
		 * 
		 * @return
		 * @throws IOException
		 */
		private String read() throws IOException {
			StringBuffer content = new StringBuffer();
			boolean zeroByteRead = false;
			do {
				int codePoint = socketIn.read();
				if ((codePoint == 0) || (codePoint == -1)) {
					zeroByteRead = true;
				} else {
					content.appendCodePoint(codePoint);
				}
			} while ((!(zeroByteRead)) && (content.length() < 100));
			return content.toString().trim();
		}

		/**
		 * 向客户端输出内容
		 * 
		 * @param str
		 *            内容信息
		 * @throws IOException
		 */
		private void write(String str) throws IOException {
			socketOut.println(str);
			socketOut.flush();
		}

		/**
		 * 关闭连接
		 */
		private void close() {
			try {
				if (socketOut != null) {
					socketOut.close();
				}
				if (socketIn != null) {
					socketIn.close();
				}
				if (socket != null) {
					socket.close();
				}
			} catch (Exception e) {
				log.error("关闭连接出错，原因：", e);
			} finally {
				socketOut = null;
				socketIn = null;
				socket = null;
			}
		}
	}

	/**
	 * 线程池状态日志
	 */
	private class PoolStatusLogger extends TimerTask {
		public void run() {
			log.info("-----------------THREAD POOL STATUS-----------------");
			log.info("Total received task count since startup: "
					+ threadPool.getTaskCount());
			log.info("Total completed task count since startup: "
					+ threadPool.getCompletedTaskCount());
			log.info("Current active task count:" + threadPool.getActiveCount());
			log.info("Largest Pool Size Reached:"
					+ threadPool.getLargestPoolSize());
			log.info("Current Pool Size:" + threadPool.getPoolSize());
			log.info("----------------------------------------------------");
		}
	}
}